package websocket

import (
	"errors"
	"fmt"
	"github.com/gorilla/websocket"
	"log"
	"net/http"
	"sync"
	"time"
)

type Connection struct {
	wsConn    *websocket.Conn
	inChan    chan []byte
	outChan   chan []byte
	closeChan chan byte
	isClosed  bool
	lock      sync.Mutex
	isServer  bool
	header    http.Header
	serverId  string
}

func InitConnection(wsConn *websocket.Conn, isServer bool, header http.Header) (conn *Connection, err error) {
	if wsConn == nil {
		return
	}
	conn = &Connection{
		wsConn:    wsConn,
		inChan:    make(chan []byte, 1000),
		outChan:   make(chan []byte, 1000),
		closeChan: make(chan byte, 1),
		isServer:  isServer,
		header:    header,
	}
	go conn.readLoop()
	go conn.writeLoop()
	if isServer && header != nil {
		serverId := header.Get("serverId")
		conn.serverId = serverId
	}
	return
}

func (conn *Connection) ReadMessage() (data []byte, err error) {
	select {
	case data = <-conn.inChan:
		conn.wsConn.SetReadDeadline(time.Now().Add(20 * time.Second))
	case <-conn.closeChan:
		err = errors.New(fmt.Sprintf("read connection is closed"))
	}
	return
}
func (conn *Connection) WriteMessage(data []byte) (err error) {
	select {
	case conn.outChan <- data:
		//conn.wsConn.SetWriteDeadline(time.Now().Add(10 * time.Second))
	case <-conn.closeChan:
		err = errors.New(fmt.Sprintf("write connection is closed"))
		log.Println(err)
	}
	return
}

func (conn *Connection) Close() {
	if conn == nil || conn.wsConn == nil {
		return
	}
	//线程安全,可以重入的close(可多次执行)
	conn.wsConn.Close()
	//通道的close只能执行一次 需要枷锁
	conn.lock.Lock()
	if !conn.isClosed {
		close(conn.closeChan)
		conn.isClosed = true
		if conn.isServer {
			log.Printf("clientId:[%s] connection close successful", WsMap.Get(conn.serverId))
			WsMap.Delete(conn.serverId)
		}
	}
	conn.lock.Unlock()
}
func (conn *Connection) IsClosed() bool {
	return conn.isClosed
}

func (conn *Connection) CloseChan() chan byte {
	return conn.closeChan
}
func (conn *Connection) readLoop() {
	var (
		data []byte
		err  error
	)
	for {
		if _, data, err = conn.wsConn.ReadMessage(); err != nil {
			if conn.isServer {
				clientId := WsMap.Get(conn.serverId).(string)
				log.Printf("wsConn read clientId:[%s] message error:[%v]", clientId, err)
			} else {

			}
			goto ERR
		}
		select {
		case conn.inChan <- data:
		case <-conn.closeChan:
			//closeChan关闭的时候回进入到这个case
			goto ERR
		case <-time.After(5 * time.Second):
			goto ERR
		}
	}
ERR:
	conn.Close()
}
func (conn *Connection) writeLoop() {
	var (
		data []byte
		err  error
	)
	for {
		select {
		case data = <-conn.outChan:
		case <-conn.closeChan:
			goto ERR
		}
		if err = conn.wsConn.WriteMessage(websocket.TextMessage, data); err != nil {
			log.Printf("wsConn write message error:[%v]", err)
			goto ERR
		}
	}

ERR:
	conn.Close()
}
